package com.youlu.campus.service.vote;

import com.youlu.campus.entity.vote.VoteConstant;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.commons.lang3.tuple.Triple;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Service;
import redis.clients.jedis.Tuple;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author : [dengbo]
 * @className : ActivityVoteRedisService
 * @description : [活动投票service]
 * @createTime : [2022/6/28 13:58]
 */
@Service
@Slf4j
public class ActivityVoteRedisService {

    @Autowired
    private StringRedisTemplate redisTemplate;

    public boolean hasVote() {
        return true;
    }

    public String getAllOrgVoteKey(String activityId) {
        String str = "user.activityId:%s.all.school";
        return String.format(str, activityId);
    }

    public String getVoteActivityIdPVKey(String activityId) {
        String str = "vote.activityId:%s.pv";
        return String.format(str, activityId);
    }

    public String getUserPerDayReceiveKey(String activityId, LocalDate date) {
        String dateStr = date.format(DateTimeFormatter.ofPattern("yyyyMMdd"));
        String str = "vote.activityId:%s.perday:%s.receive";
        return String.format(str, activityId, dateStr);
    }

    public String getTotalOrgVoteKey(String activityId) {
        String str = "user.activityId:%s.total.vote";
        return String.format(str, activityId);
    }

    public String getProvinceOrgVoteKey(String activityId, String proCode) {
        String str = "user.activityId:%s.province:%s";
        return String.format(str, activityId, proCode);
    }

    public String getWeekOrgVoteKey(String activityId, String week) {
        String str = "user.activityId:%s.week:%s";
        return String.format(str, activityId, week);
    }

    public String getUserPersonalVoteKey(String activityId) {
        String str = "user.activityId:%s.personal";
        return String.format(str, activityId);
    }

    /**
     * 添加每天领取数据
     */
    public void addPerDayReceive(String activityId, LocalDate date, String userId, int num) {
        try {
            String key = getUserPerDayReceiveKey(activityId, date);
            boolean exist = redisTemplate.hasKey(key);
            redisTemplate.opsForZSet().add(key, userId, num);
            if (!exist) {
                redisTemplate.expire(key, VoteConstant.PER_DAY_VOTE_EXPIRE_TIME, TimeUnit.SECONDS);
            }
        } catch (Exception e) {
            log.error("addPerDayReceive,redis error", e);
        }
    }

    /**
     * 更新投票扣减
     */
    public void incPerDayReceive(String activityId, LocalDate date, String userId, int num) {
        try {
            String key = getUserPerDayReceiveKey(activityId, date);
            redisTemplate.opsForZSet().incrementScore(key, userId, num);
        } catch (Exception e) {
            log.error("addPerDayReceive,redis error", e);
        }
    }

    /**
     * 获取投票扣减
     */
    public long getPerDayReceive(String activityId, LocalDate date, String userId) {
        try {
            String key = getUserPerDayReceiveKey(activityId, date);
            Double score = redisTemplate.opsForZSet().score(key, userId);
            return score == null ? 0L : score.longValue();
        } catch (Exception e) {
            log.error("addPerDayReceive,redis error", e);
        }
        return VoteConstant.PER_DAY_RECEIVE_NUM;
    }

    /**
     * 判断是否有每日领取记录
     */
    public boolean hasPerDayReceive(String activityId, LocalDate date, String userId) {
        try {
            String key = getUserPerDayReceiveKey(activityId, date);
            return redisTemplate.opsForZSet().rank(key, userId) != null;
        } catch (Exception e) {
            log.error("addPerDayReceive,redis error", e);
        }
        return false;
    }

    /**
     * 增加组织投票
     */
    public double incAllSchoolVote(String activityId, String orgCode, int num) {
        return redisTemplate.opsForZSet().incrementScore(getAllOrgVoteKey(activityId), orgCode, num);
    }

    public long getAllVoteSchoolNum(String activityId) {
        Long num = redisTemplate.opsForZSet().zCard(getAllOrgVoteKey(activityId));
        return num == null ? 0 : num;
    }

    public Triple<String, Long, Double> getAllUserVoteRankAndScore(String activityId, String orgCode) {
        String key = getAllOrgVoteKey(activityId);
        return getRankAndScore(key, orgCode);
    }

    public Pair<String, Long> getAllUserVoteRank(String activityId, String orgCode) {
        String key = getAllOrgVoteKey(activityId);
        Long rank = redisTemplate.opsForZSet().reverseRank(key, orgCode);
        return Pair.of(orgCode, rank);
    }

    public Pair<Long, Set<Tuple>> getAllOrgPageInfo(String activityId, int page, int pageSize) {
        String key = getAllOrgVoteKey(activityId);
        return getRankPageInfo(key, page, pageSize);
    }

    public Pair<Long, Set<Tuple>> getProvinceOrgPageInfo(String activityId, String proCode, int page, int pageSize) {
        String key = getProvinceOrgVoteKey(activityId, proCode);
        return getRankPageInfo(key, page, pageSize);
    }

    /**
     * 增加省份投票数量
     */
    public double incProvinceSchoolVote(String activityId, String proCode, String orgCode, int num) {
        return redisTemplate.opsForZSet().incrementScore(getProvinceOrgVoteKey(activityId, proCode), orgCode, num);
    }

    public double getUserProvinceSchoolVote(String activityId, String proId, String orgCode) {
        Double score = redisTemplate.opsForZSet().score(getProvinceOrgVoteKey(activityId, proId), orgCode);
        return score == null ? 0 : score;
    }

    public Triple<String, Long, Double> getUserProvinceSchoolVoteRankAndScore(String activityId, String proCode, String orgCode) {
        String key = getProvinceOrgVoteKey(activityId, proCode);
        return getRankAndScore(key, orgCode);
    }

    /**
     * 批量获取组织投票排名和分数（保持返回值类型不变）
     */
    public List<Triple<String, Long, Double>> getOrgVoteRankAndScoreList(String activityId, List<String> orgCodeList) {
        String key = getAllOrgVoteKey(activityId);
        ZSetOperations<String, String> zSetOps = redisTemplate.opsForZSet();

        List<Triple<String, Long, Double>> result = new ArrayList<>(orgCodeList.size());
        for (String orgCode : orgCodeList) {
            Double score = zSetOps.score(key, orgCode);
            Long rank = zSetOps.reverseRank(key, orgCode);
            result.add(Triple.of(orgCode, rank, score));
        }
        return result;
    }

    public String getWeekStr() {
        LocalDate date = LocalDate.now();
        return date.getYear() + "-" + date.getDayOfWeek();
    }

    public double addWeekSchoolVote(String activityId, String week, String orgCode) {
        return redisTemplate.opsForZSet().incrementScore(getWeekOrgVoteKey(activityId, week), orgCode, 1);
    }

    public double getUserWeekSchoolVote(String activityId, String week, String orgCode) {
        Double score = redisTemplate.opsForZSet().score(getWeekOrgVoteKey(activityId, week), orgCode);
        return score == null ? 0 : score;
    }

    /**
     * 添加用户投票数量
     */
    public double incPersonalVote(String activityId, String userId, int num) {
        return redisTemplate.opsForZSet().incrementScore(getUserPersonalVoteKey(activityId), userId, num);
    }

    public Pair<String, Long> getUserPersonalVoteRank(String activityId, String userId) {
        String key = getUserPersonalVoteKey(activityId);
        Long rank = redisTemplate.opsForZSet().reverseRank(key, userId);
        return Pair.of(userId, rank);
    }

    public Pair<Long, Set<Tuple>> getUserPersonalPageInfo(String activityId, int page, int pageSize) {
        String key = getUserPersonalVoteKey(activityId);
        return getRankPageInfo(key, page, pageSize);
    }

    /**
     * 获取排名分页信息（修正：通过 Tuple 构造器创建对象，解决构造器报错）
     */
    public Pair<Long, Set<Tuple>> getRankPageInfo(String key, int page, int pageSize) {
        long offset = (page - 1L) * pageSize;
        long end = offset + pageSize - 1;

        // 1. 获取 Spring Redis 的 TypedTuple 集合
        Set<ZSetOperations.TypedTuple<String>> typedTuples = redisTemplate.opsForZSet()
                .reverseRangeWithScores(key, offset, end);

        // 2. 转换为 Jedis Tuple 集合（核心修正：使用 Tuple 构造器）
        Set<Tuple> jedisTuples = convertToJedisTuples(typedTuples);

        // 3. 获取总数
        Long total = redisTemplate.opsForZSet().zCard(key);
        return Pair.of(total == null ? 0 : total, jedisTuples);
    }

    public Triple<String, Long, Double> getUserPersonalVoteRankAndScore(String activityId, String userId) {
        String key = getUserPersonalVoteKey(activityId);
        return getRankAndScore(key, userId);
    }

    public Triple<String, Long, Double> getRankAndScore(String key, String memId) {
        ZSetOperations<String, String> zSetOps = redisTemplate.opsForZSet();
        Double score = zSetOps.score(key, memId);
        Long rank = zSetOps.reverseRank(key, memId);
        return Triple.of(memId, rank, score);
    }

    public Long incActivityPV(String activityId) {
        return redisTemplate.opsForValue().increment(getVoteActivityIdPVKey(activityId), 1);
    }

    /**
     * 增加组织总的投票数量
     */
    public double incTotalVoteNum(String activityId, int num) {
        return redisTemplate.opsForValue().increment(getTotalOrgVoteKey(activityId), num);
    }

    public long getTotalVoteNum(String activityId) {
        String value = redisTemplate.opsForValue().get(getTotalOrgVoteKey(activityId));
        return StringUtils.isBlank(value) ? 0 : Long.parseLong(value);
    }

    public long getActivityPV(String activityId) {
        String value = redisTemplate.opsForValue().get(getVoteActivityIdPVKey(activityId));
        return StringUtils.isBlank(value) ? 0 : Long.parseLong(value);
    }

    // ------------------------------ 内部工具方法 ------------------------------

    /**
     * 修正：通过 Jedis Tuple 的构造器（String + Double）创建对象，解决构造器不匹配问题
     */
    private Set<Tuple> convertToJedisTuples(Set<ZSetOperations.TypedTuple<String>> typedTuples) {
        if (typedTuples == null || typedTuples.isEmpty()) {
            return Collections.emptySet();
        }

        Set<Tuple> jedisTuples = new HashSet<>(typedTuples.size());
        for (ZSetOperations.TypedTuple<String> typedTuple : typedTuples) {
            String element = typedTuple.getValue();
            Double score = typedTuple.getScore();
            // 直接使用 Tuple 构造器创建对象（Jedis 原生支持 String + Double 入参）
            jedisTuples.add(new Tuple(element, score));
        }
        return jedisTuples;
    }
}